package gov.nasa.anml.utility;

import java.util.Arrays;

public final class Utility {
	private Utility() {
	}

	public static boolean deepEquals(Object a,Object b) {
		if (a==b)
			return true;
		if (a==null || b==null)
			return false;

		Class aClass = a.getClass();
		if (!aClass.isArray())
			return a.equals(b);

		Class bClass = b.getClass();
		
		if (aClass == byte[].class) {
			if (bClass != byte[].class)
				return false;

			byte[] aCast = (byte[]) a;
			byte[] bCast = (byte[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == short[].class) { 
			if (bClass != short[].class)
				return false;

			short[] aCast = (short[]) a;
			short[] bCast = (short[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;			
		}
		else if (aClass == int[].class) {
			if (bClass != int[].class)
				return false;
			
			int[] aCast = (int[]) a;
			int[] bCast = (int[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == long[].class) {
			if (bClass != long[].class)
				return false;
			
			long[] aCast = (long[]) a;
			long[] bCast = (long[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == char[].class) {
			if (bClass != char[].class)
				return false;
			
			char[] aCast = (char[]) a;
			char[] bCast = (char[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == float[].class) {
			if (bClass != float[].class)
				return false;
			
			float[] aCast = (float[]) a;
			float[] bCast = (float[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == double[].class) {
			if (bClass != double[].class)
				return false;
			
			double[] aCast = (double[]) a;
			double[] bCast = (double[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else if (aClass == boolean[].class) {
			if (bClass != boolean[].class)
				return false;
			
			boolean[] aCast = (boolean[]) a;
			boolean[] bCast = (boolean[]) b;
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (aCast[i] != bCast[i])
					return false;
		}
		else {
			Object[] aCast = (Object[]) a;
			Object[] bCast = (Object[]) b;
			
			int length = aCast.length;
			if (bCast.length != length)
				return false;

			for (int i=0; i<length; i++)
				if (!aCast[i].equals(bCast[i]))
					return false;
		}
		return true;
	}

	// see Arrays.deepHashCode
	// this should be better than that
	public static int hash(Object o) {
		int result=1;
		if (o instanceof Object[]) {
			for (Object e : (Object[]) o)
				result = 31 * result + hash(e);
			return result;
		}
		
		Class<?> c = o.getClass();

		// ids
		if (c == int[].class) {
			int[] a = (int[]) o;
			for (int e : a)
				result = 31 * result + e;
			return result;
		}
		
		// strings
		if (c == char[].class) {
			char[] a = (char[]) o;
			for (char e : a)
				result = 31 * result + e;
			return result;
		}
		
		// raw data (magic numbers, etc.)
		if (c == byte[].class) {
			byte[] a = (byte[]) o;
			for (byte e : a)
				result = 31 * result + e;
			return result;
		}

		// property vectors.  Probably BitSet style is better than hashing though
		if (c == boolean[].class) {
			boolean[] a = (boolean[]) o;
			for (boolean e : a)
	            result = 31 * result + (e ? 1231 : 1237);
			return result;
		}

		// uncommon variations on int
		if (c == short[].class) {
			short[] a = (short[]) o;
			for (short e : a)
				result = 31 * result + e;
			return result;
		}
		
		if (c == long[].class) {
			long[] a = (long[]) o;
			for (long e : a)
				result = 31 * result + (int) (e ^ (e >>> 32));
			return result;
		}
		
		// brittle
		if (c == float[].class) {
			float[] a = (float[]) o;
			for (float e : a)
				result = 31 * result + Float.floatToIntBits(e);
			return result;
		}
		
		// still brittle
		if (c == double[].class) {
			double[] a = (double[]) o;
			for (double d : a) {
				long e = Double.doubleToLongBits(d);
				result = 31 * result + (int) (e ^ (e >>> 32));
			}
			return result;
		}
		
		if (o != null)
			return o.hashCode();
		
		return 0;
	}

	public static int hash(char[] val) {
		int h=0;
		int off = 0;
		int len = val.length;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return h;
	}

	public static int hash(char[] val,int len) {
		int h=0;
		int off = 0;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return h;
	}
	public static int hash(int[] val) {
		int h=0;
		int off = 0;
		int len = val.length;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return h;
	}

	public static int hash(int[] val,int len) {
		int h=0;
		int off = 0;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return h;
	}
	public static int hash(float[] val) {
		float h=0;
		int off = 0;
		int len = val.length;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return (int) h;
	}

	public static int hash(float[] val,int len) {
		float h=0;
		int off = 0;

		for (int i = 0; i < len; i++) {
			h = 31 * h + val[off++];
		}
		return (int) h;
	}


}
